package com.fept.msgcore.msgserver.netty.handler;

import com.fept.msgcore.msgserver.protocol.frame.Frame;
import com.fept.msgcore.msgserver.protocol.frame.ProtocolMessage;
import com.fept.msgcore.msgserver.protocol.model.header.Header;
import com.fept.msgcore.msgserver.util.system.ByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;

/**
 * Created by Srma on 1/13/2017.
 */
public class ProtocolMessageDecoder extends ByteToMessageDecoder {

    private static Logger logger = LoggerFactory.getLogger(ProtocolMessageDecoder.class);

    private static final int PROTOCOL_MESSAGE_DECODER_STATE_HEADER = 0x00;
    private static final int PROTOCOL_MESSAGE_DECODER_STATE_DATA = 0x01;

    private String name;
    private int state;
    private byte[] header = new byte[Header.getHeaderLength()];
    private byte[] data;

    /**
     *
     * @param name
     */
    public ProtocolMessageDecoder(String name) {
        this.setName(name);
        this.setState(PROTOCOL_MESSAGE_DECODER_STATE_HEADER);
    }

    /**
     *
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in,
                          MessageBuf<Object> mbuf) {

        try {
            InetAddress hostIPAddress = ((InetSocketAddress) ctx.channel()
                    .remoteAddress()).getAddress();
            String hostIP = hostIPAddress.getHostAddress();
            int hostPort = ((InetSocketAddress) ctx.channel().remoteAddress())
                    .getPort();

            while (in.readableBytes() > 0) {
                if (this.state == ProtocolMessageDecoder.PROTOCOL_MESSAGE_DECODER_STATE_HEADER) {
                    if (in.readableBytes() < Header.getHeaderLength()) {
                        break;
                    }
                    in.readBytes(this.header);
                    this.state = ProtocolMessageDecoder.PROTOCOL_MESSAGE_DECODER_STATE_DATA;
                }
                if (this.state == ProtocolMessageDecoder.PROTOCOL_MESSAGE_DECODER_STATE_DATA) {
                    int len = ProtocolMessage.frameLength(this.header);
                    if (in.readableBytes() < len) {
                        break;
                    }
                    if (len == 0) {
                        this.data = new byte[0];
                    } else {
                        this.data = new byte[len];
                        in.readBytes(this.data);
                    }

                    /**
                     * record raw message
                     */
                    String rawMsg = ByteUtil.dumpMessage(this.header,
                            this.data, 0);
//                    LogHelper.logRawMsg(String.format("[from: %s %d]: %s",
//                            hostIP, hostPort, rawMsg));

                    /**
                     * unpacking frame
                     */
                    Frame msg = ProtocolMessage.unpacking(this.header,
                            this.data);

                    /**
                     * 设置IP，端口，接收时间等信息
                     */
                    msg.setIp(hostIP);
                    msg.setPort(hostPort);
                    msg.setReceivedTime(System.currentTimeMillis());

                    /**
                     * log readable messages
                     */
                    logger.info(String.format("[from: %s %d]: %s",
                            hostIP, hostPort, msg.toString()));

                    mbuf.add(msg);
                    this.state = PROTOCOL_MESSAGE_DECODER_STATE_HEADER;
                }
            }

        }
        catch (Exception e) {
            logger.error(e.getMessage());
            ctx.channel().close();
        }

    }

    /**
     *
     * @return
     */
    public int getState() {
        return state;
    }

    /**
     *
     * @param state
     */
    public void setState(int state) {
        this.state = state;
    }

    /**
     *
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     *
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
}
